package is.glif.fenius.ws.draft.impl;

import is.glif.fenius.api.FActivationStatus;
import is.glif.fenius.api.FCreateReq;
import is.glif.fenius.api.FCreateResp;
import is.glif.fenius.api.FListReq;
import is.glif.fenius.api.FListResp;
import is.glif.fenius.api.FCancelReq;
import is.glif.fenius.api.FCancelResp;
import is.glif.fenius.api.FReservation;
import is.glif.fenius.api.FReservationReqParams;
import is.glif.fenius.api.FReservationStatus;
import is.glif.fenius.api.Translator;
import is.glif.fenius.api.TranslatorFactory;
import is.glif.fenius.ws.draft.gen.Avp;
import is.glif.fenius.ws.draft.gen.AvpSeq;
import is.glif.fenius.ws.draft.gen.Cancel;
import is.glif.fenius.ws.draft.gen.CancelReq;
import is.glif.fenius.ws.draft.gen.CancelResp;
import is.glif.fenius.ws.draft.gen.CancelResponse;
import is.glif.fenius.ws.draft.gen.CommonParams;
import is.glif.fenius.ws.draft.gen.Create;
import is.glif.fenius.ws.draft.gen.CreateReq;
import is.glif.fenius.ws.draft.gen.CreateResp;
import is.glif.fenius.ws.draft.gen.CreateResponse;
import is.glif.fenius.ws.draft.gen.DraftFeniusService;
import is.glif.fenius.ws.draft.gen.EndpointPair;
import is.glif.fenius.ws.draft.gen.ExternalFault_Exception;
import is.glif.fenius.ws.draft.gen.InternalFault_Exception;
import is.glif.fenius.ws.draft.gen.List;
import is.glif.fenius.ws.draft.gen.ListReq;
import is.glif.fenius.ws.draft.gen.ListResp;
import is.glif.fenius.ws.draft.gen.ListResponse;
import is.glif.fenius.ws.draft.gen.ReservationParams;
import is.glif.fenius.ws.draft.gen.Status;
import is.glif.fenius.ws.draft.gen.TimeParams;

import java.util.Date;
import java.util.GregorianCalendar;
import java.util.logging.Logger;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

import com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl;

@javax.jws.WebService(
                      serviceName = "draft",
                      portName = "draftFeniusServicePort",
                      targetNamespace = "http://fenius.glif.is/ws/draft/",
                      endpointInterface = "is.glif.fenius.ws.draft.gen.DraftFeniusService")

public class DraftFeniusServiceImpl implements DraftFeniusService {

    private static final Logger LOG = Logger.getLogger(DraftFeniusServiceImpl.class.getName());

    public CreateResponse create(Create parameters) throws InternalFault_Exception , ExternalFault_Exception    {
        LOG.info("Executing operation create");
        System.out.println(parameters);
        try {
            Translator t = TranslatorFactory.getInstance().getTranslator();
            if (t == null) {
                InternalFault_Exception ex = new InternalFault_Exception("Could not locate translator service");
                throw ex;
            }

            CreateResponse _return = new CreateResponse();
            CreateReq req = parameters.getArg0();

            ReservationParams reqParams = req.getResvParams();
            FReservationReqParams fReqParams = this.mapSoapResvToInternal(reqParams);
            FCreateReq internalReq = new FCreateReq();
            internalReq.setResvParams(fReqParams);

            FCreateResp fResp = t.create(internalReq);

            CreateResp resp = new CreateResp();

            FReservation fRespParams = fResp.getResvParams();
            ReservationParams respParams = this.mapInternalResvToSoap(fRespParams);
            resp.setResvParams(respParams);

            _return.setReturn(resp);
            return _return;
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

    public CancelResponse cancel(Cancel parameters) throws InternalFault_Exception , ExternalFault_Exception    {
        LOG.info("Executing operation cancel");
        System.out.println(parameters);
        try {
            Translator t = TranslatorFactory.getInstance().getTranslator();
            if (t == null) {
                InternalFault_Exception ex = new InternalFault_Exception("Could not locate translator service");
                throw ex;
            }

            CancelReq req = parameters.getArg0();
            String reservationId = req.getReservationId();

            FCancelReq fReq = new FCancelReq();
            fReq.setReservationId(reservationId);
            FCancelResp fResp = t.cancel(fReq);

            // FIXME
            Status status = Status.CANCELLED;


            CancelResponse _return = new CancelResponse();
            CancelResp resp = new CancelResp();
            resp.setStatus(status);
        _return.setReturn(resp);
            return _return;
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

    private Status mapStatus (FReservationStatus rStatus, FActivationStatus aStatus) throws InternalFault_Exception {
        if (aStatus.value().equals(Status.ACTIVE.toString())) {
            return Status.ACTIVE;
        } else if (aStatus.value().equals(Status.FAILED.toString())) {
            return Status.FAILED;
        } else if (aStatus.equals(FActivationStatus.DEACTIVATING)) {
            return Status.INTEARDOWN;
        } else if (aStatus.equals(FActivationStatus.ACTIVATING)) {
            return Status.INSETUP;
        } else if (aStatus.equals(FActivationStatus.INACTIVE)) {
            if (rStatus.equals(FReservationStatus.FAILED)) {
                return Status.FAILED;
            } else if (rStatus.equals(FReservationStatus.FINISHING) || rStatus.equals(FReservationStatus.FINISHED)) {
                return Status.FINISHED;
            } else if (rStatus.equals(FReservationStatus.RELEASING) || rStatus.equals(FReservationStatus.RELEASED)) {
                return Status.CANCELLED;
            } else if (rStatus.equals(FReservationStatus.RESERVING) ) {
                return Status.REQUESTING;
            } else if (rStatus.equals(FReservationStatus.RESERVED) ) {
                return Status.RESERVED;
            } else {
                throw new InternalFault_Exception("Invalid status: "+rStatus);
            }
        } else {
            throw new InternalFault_Exception("Invalid status: "+aStatus);
        }
    }


    public ListResponse list(List parameters) throws InternalFault_Exception , ExternalFault_Exception    {
        LOG.info("Executing operation list");
        System.out.println(parameters);
        try {
            Translator t = TranslatorFactory.getInstance().getTranslator();
            if (t == null) {
                InternalFault_Exception ex = new InternalFault_Exception("Could not locate translator service");
                throw ex;
            }

            ListReq req = parameters.getArg0();


            FListReq fReq = new FListReq();
            FListResp fResp = t.list(fReq);
            ListResp resp = new ListResp();
            if (fResp == null) {
                System.out.println("No result.");
            } else {

                for (FReservation fRespParams : fResp.getReservations()) {
                    ReservationParams respParams = this.mapInternalResvToSoap(fRespParams);
                    resp.getReservations().add(respParams);
                }
            }

            ListResponse _return = new ListResponse();
            _return.setReturn(resp);

            return _return;
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }







    private ReservationParams mapInternalResvToSoap(FReservation fParams) throws InternalFault_Exception {
        ReservationParams params = new ReservationParams();

        EndpointPair srcDest = new EndpointPair();
        srcDest.setA(fParams.getSrcEndpoint());
        srcDest.setZ(fParams.getDstEndpoint());
        params.setEndpoints(srcDest);


        Date fStart = fParams.getActivationTime();
        Integer duration = fParams.getDuration();
        GregorianCalendar gcStart = (GregorianCalendar) GregorianCalendar.getInstance();
        GregorianCalendar gcEnd = (GregorianCalendar) GregorianCalendar.getInstance();

        gcStart.setTime(fStart);
        Long millisStart = gcStart.getTimeInMillis();
        Long millisEnd = millisStart + (duration * 1000);
        gcEnd.setTimeInMillis(millisEnd);


        XMLGregorianCalendar startTime = new XMLGregorianCalendarImpl(gcStart);
        XMLGregorianCalendar endTime = new XMLGregorianCalendarImpl(gcEnd);

        CommonParams cp = new CommonParams();
        TimeParams tp = new TimeParams();
        tp.setScheduledStartTime(startTime);
        tp.setScheduledEndTime(endTime);
        cp.setTimeParams(tp);
        cp.setBandwidth(fParams.getBandwidth());

        Avp srcVlanTag = new Avp();
        srcVlanTag.setAttrName("eth.srcVlan");
        srcVlanTag.setAttrVal(fParams.getSrcVlanTag());


        Avp dstVlanTag = new Avp();
        dstVlanTag.setAttrName("eth.dstVlan");
        dstVlanTag.setAttrVal(fParams.getDstVlanTag());


        AvpSeq adhoc = new AvpSeq();
        adhoc.getAvp().add(srcVlanTag);
        adhoc.getAvp().add(dstVlanTag);

        cp.setAdhocParams(adhoc);
        params.setEndtoendParams(cp);
        params.setReservationId(fParams.getReservationId());
        Status st = this.mapStatus(fParams.getReservationStatus(), fParams.getActivationStatus());
        cp.setStatus(st);
        return params;
    }


    private FReservationReqParams mapSoapResvToInternal(ReservationParams params) {
        FReservationReqParams fParams = new FReservationReqParams();
        String srcEndpoint = params.getEndpoints().getA();
        String dstEndpoint = params.getEndpoints().getZ();
        Integer bandwidth = params.getEndtoendParams().getBandwidth();
        CommonParams cp = params.getEndtoendParams();
        TimeParams tp = cp.getTimeParams();
        XMLGregorianCalendar startCal = tp.getScheduledStartTime();
        if (startCal == null) {
            startCal = tp.getRequestedStartTime();
        }
        XMLGregorianCalendar endCal = tp.getScheduledEndTime();
        if (endCal == null) {
            endCal = tp.getRequestedEndTime();
        }
        Date activationTime = startCal.toGregorianCalendar().getTime();
        Long startMillis = startCal.toGregorianCalendar().getTimeInMillis();
        Long endMillis   = endCal.toGregorianCalendar().getTimeInMillis();
        Long durationMillis = endMillis - startMillis;
        Long durationSecLng = durationMillis / 1000;
        Integer durationSec = durationSecLng.intValue();
        String status = params.getEndtoendParams().getStatus().toString();
        String reservationId = params.getReservationId();
        AvpSeq adhoc = params.getEndtoendParams().getAdhocParams();
        for (Avp avp : adhoc.getAvp()) {
            if (avp.getAttrName().equals("eth.srcVlan")) {
                fParams.setSrcVlanTag(avp.getAttrVal());
            }
            if (avp.getAttrName().equals("eth.dstVlan")) {
                fParams.setDstVlanTag(avp.getAttrVal());
            }
        }


        fParams.setSrcEndpoint(srcEndpoint);
        fParams.setDstEndpoint(dstEndpoint);
        fParams.setBandwidth(bandwidth);
        fParams.setDuration(durationSec);

        fParams.setActivationTime(activationTime);
        return fParams;
    }

}
